﻿using System;
using System.Diagnostics;
using System.Threading;
using AzureStorageMapper.TableStorage;
using AzureStorageMapper.TableStorage.ActionModes;
using AzureStorageMapper.TableStorage.Attributes;
using NUnit.Framework;

namespace AzureStorageMapper.Tests.TableStorage.TableContextTests
{
    [TestFixture]
    public class CRUDTests
    {
        const string DefaultPartitionKey = "default";
        TableContext<SampleTableModel> tc = new TableContext<SampleTableModel>();
        
        [SetUp]
        public void SetUpTest()
        {
            Trace.TraceInformation("Creatinig Table ...");
            tc.CreateTable();

            Trace.TraceInformation("Sleep for 10 seconds ...");
            Thread.Sleep(TimeSpan.FromSeconds(10));
        }

        [TearDown]
        public void TearDownTest()
        {
            Trace.TraceInformation("Sleep for 10 seconds before deleting table ...");
            Thread.Sleep(TimeSpan.FromSeconds(10));

            Trace.TraceInformation("Deleting Table ...");
            tc.DeleteTable();
        }

        [Test]
        public void CURDTest()
        {
            SampleTableModel modelInit = new SampleTableModel(DefaultPartitionKey, Guid.NewGuid().ToString());
            modelInit.ColumnOne = "col 1";
            modelInit.ColumnTwo = 1;
            modelInit.ColumnThree = 1L;
            tc.Save(modelInit);

            Trace.TraceInformation("Retriving saved data and compare ...");
            SampleTableModel readModelInit = tc.FindEntity(modelInit.PartitionKey, modelInit.RowKey);
            Assert.AreEqual(modelInit.ColumnOne, readModelInit.ColumnOne);
            Assert.AreEqual(modelInit.ColumnTwo, readModelInit.ColumnTwo);
            Assert.AreEqual(modelInit.ColumnThree, readModelInit.ColumnThree);

            Trace.TraceInformation("Replacing data schema ...");
            TableContext<SampleTableModel2> tc2 = new TableContext<SampleTableModel2>();
            SampleTableModel2 replaceSchemaModel = new SampleTableModel2(modelInit.PartitionKey, modelInit.RowKey);
            replaceSchemaModel.ColumnFour = modelInit.ColumnOne;
            replaceSchemaModel.ColumnFive = modelInit.ColumnTwo;
            tc2.Save(replaceSchemaModel, SaveMode.InsertOrReplace);
            
            Trace.TraceInformation("Sleep for 5 seconds ...");
            Thread.Sleep(TimeSpan.FromSeconds(5));

            Trace.TraceInformation("Check old columns, assert empty or null");
            SampleTableModel readReplacedModel = tc.FindEntity(modelInit.PartitionKey, modelInit.RowKey);
            Assert.AreEqual(null, readReplacedModel.ColumnOne);
            Assert.AreEqual(0, readReplacedModel.ColumnTwo);
            Assert.AreEqual(0, readReplacedModel.ColumnThree);

            Trace.TraceInformation("Merging schema ...");
            tc.Save(modelInit, SaveMode.InsertOrMerge);
            
            Trace.TraceInformation("Sleep for 5 seconds ...");
            Thread.Sleep(TimeSpan.FromSeconds(5));

            Trace.TraceInformation("Retriving merged enties, comparing columns ...");
            TableContext<SampleTableModel3> tc3 = new TableContext<SampleTableModel3>();
            SampleTableModel3 totalSchemaModel = tc3.FindEntity(modelInit.PartitionKey, modelInit.RowKey);
            Assert.AreEqual(modelInit.ColumnOne, totalSchemaModel.ColumnOne);
            Assert.AreEqual(modelInit.ColumnTwo, totalSchemaModel.ColumnTwo);
            Assert.AreEqual(modelInit.ColumnThree, totalSchemaModel.ColumnThree);
            Assert.AreEqual(replaceSchemaModel.ColumnFour, totalSchemaModel.ColumnFour);
            Assert.AreEqual(replaceSchemaModel.ColumnFive, totalSchemaModel.ColumnFive);

            Trace.TraceInformation("[Update] Shrinking columns ...");
            SampleTableModel2 shrinkedModel = new SampleTableModel2(totalSchemaModel.PartitionKey, totalSchemaModel.RowKey);
            shrinkedModel.ColumnFour = "shrinked model";
            shrinkedModel.ColumnFive = 555;
            tc2.Update(shrinkedModel);

            Trace.TraceInformation("Sleep for 5 seconds ...");
            Thread.Sleep(TimeSpan.FromSeconds(5));
            
            Trace.TraceInformation("Comparing updated schema ...");
            SampleTableModel3 readTotal = tc3.FindEntity(totalSchemaModel.PartitionKey, totalSchemaModel.RowKey);
            Assert.AreEqual(null, readTotal.ColumnOne);
            Assert.AreEqual(0, readTotal.ColumnTwo);
            Assert.AreEqual(0, readTotal.ColumnThree);
            Assert.AreEqual(shrinkedModel.ColumnFour, readTotal.ColumnFour);
            Assert.AreEqual(shrinkedModel.ColumnFive, readTotal.ColumnFive);

            Trace.TraceInformation("[Update] Expanding columns ...");
            totalSchemaModel.ColumnFour = "Expanding ...";
            totalSchemaModel.ColumnFive = shrinkedModel.ColumnFive;
            tc3.Update(totalSchemaModel, UpdateMode.Merge);

            Trace.TraceInformation("Retriving merged enties, comparing columns ...");
            SampleTableModel3 totalMerge = tc3.FindEntity(modelInit.PartitionKey, modelInit.RowKey);
            Assert.AreEqual(totalSchemaModel.ColumnOne, totalMerge.ColumnOne);
            Assert.AreEqual(totalSchemaModel.ColumnTwo, totalMerge.ColumnTwo);
            Assert.AreEqual(totalSchemaModel.ColumnThree, totalMerge.ColumnThree);
            Assert.AreEqual(totalSchemaModel.ColumnFour, totalMerge.ColumnFour);
            Assert.AreEqual(shrinkedModel.ColumnFive, totalMerge.ColumnFive);

            Trace.TraceInformation("Sleep for 5 seconds ...");
            Thread.Sleep(TimeSpan.FromSeconds(5));

            Trace.TraceInformation("Deleting entry ...");
            tc.Delete(modelInit);
        }

        [TableModel(Name = "CRUDTests2")]
        internal class SampleTableModel : TableEntity
        {
            [TableColumn]
            public string ColumnOne { get; set; }

            [TableColumn(Name = "Column2")]
            public int ColumnTwo { get; set; }

            [TableColumn]
            public long ColumnThree { get; set; }

            public SampleTableModel(string partitionKey, string rowKey)
                : base(partitionKey, rowKey)
            {
            }
        }

        [TableModel(Name = "CRUDTests2")]
        internal class SampleTableModel2 : TableEntity
        {
            [TableColumn]
            public string ColumnFour { get; set; }

            [TableColumn]
            public int ColumnFive { get; set; }

            public SampleTableModel2(string partitionKey, string rowKey)
                : base(partitionKey, rowKey)
            {
            }
        }

        [TableModel(Name = "CRUDTests2")]
        internal class SampleTableModel3 : TableEntity
        {
            [TableColumn]
            public string ColumnOne { get; set; }

            [TableColumn(Name = "Column2")]
            public int ColumnTwo { get; set; }

            [TableColumn]
            public long ColumnThree { get; set; }

            [TableColumn]
            public string ColumnFour { get; set; }

            [TableColumn]
            public int ColumnFive { get; set; }

            public SampleTableModel3(string partitionKey, string rowKey)
                : base(partitionKey, rowKey)
            {
            }
        }
    }
}
